using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.ComponentModel;
using System.Collections.Specialized;

using miew.Lambda;
using miew.Mesh;
using miew.Enumerable;

namespace agree.Wpf.Util
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public partial class DagLayoutPanel : Panel
	{
		LayoutDag _dag;
		RailSet rails;

		public DagLayoutPanel()
		{
			_dag = new LayoutDag(this);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override void OnChildDesiredSizeChanged(UIElement child)
		{
			base.OnChildDesiredSizeChanged(child);

#if true
			foreach (LayoutDag.Node node in _dag)
			{
				node.ReMeasure();
			}
			rails = new RailSet(_dag);
			InvalidateArrange();
#else

			FrameworkElement fe = child as FrameworkElement;
			if (fe != null && _dag.ReMeasureChild(fe))
			{
				InvalidateArrange();
			}
#endif
		}

		public void FOO()
		{
			foreach (LayoutDag.Node node in _dag)
			{
				node.ReMeasure();
			}
			rails = new RailSet(_dag);
			InvalidateArrange();
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
		{
			base.OnVisualChildrenChanged(visualAdded, visualRemoved);

			FrameworkElement fe_add = visualAdded as FrameworkElement;
			if (fe_add != null && !_dag.Contains(fe_add))
				_dag.AddChild(fe_add);

			FrameworkElement fe_sub = visualRemoved as FrameworkElement;
			if (fe_sub != null && _dag.Contains(fe_sub))
				_dag.RemoveChild(fe_sub);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void ChangeNodeParents(FrameworkElement fe, IList old_list, IList new_list)
		{
			if (fe.Parent != this)
				throw new Exception();

			if (!_dag.ChangeNodeParents(fe, new_list.OfType<FrameworkElement>()))
				return;

			InvalidateArrange();
			InvalidateVisual();

#if false
			var inpc_new = new_list as INotifyCollectionChanged;
			if (inpc_new != null)
				inpc_new.CollectionChanged += (o, e) =>
					{
						Debugger.Break();
					};

			var inpc_old = old_list as INotifyCollectionChanged;
			if (inpc_old != null)
				inpc_old.CollectionChanged -= h;
#endif
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override Size MeasureOverride(Size availableSize)
		{
			if (_dag.Count == 0)
				return new Size(100, 100);

			rails = new RailSet(_dag);


			foreach (Rail r in rails)
			{
				//r.OnDagLoaded();


				//if (r.Count > 2)
				//    r.bvHorzPad.SetValue((max_width - r.Width) / (r.Count - 1));
				//else
				//r.left_start.SetValue(max_width / 2 - r.Width / 2);
			}

#if false
			//for (int j = 0; j < 15; j++)
			{
				//Debug.Print("================");
				int cmov = 0;
				int pmov = 0;
				foreach (LayoutDag.Node node in _dag)
				{
					int child_lev = node.Level + 1; ;
					if (node.Children.Count > 0 && node.Children.All(c => c.Level == child_lev && c.Parents.Count == 1))
					{
						Rail cr = rails[child_lev];

						var rgch = node.Children/*.OrderByDescending(ch => ch.AllDescendants.Count)*/.ToArray();

						/// don't bother grouping children if all children on this level are included
						if (rgch.Length != cr.Count)
						{
							double wa = rgch.WeightedAverage(n => n.railpos.RailCalcs().Center.Value, n => n.AllDescendants.Count);

							// find a starting index for the first child
							int new_ix = cr.FindIndex(wa);

							foreach (var n in rgch)
							{
								Rail.SingleNodePos rp_old = n.railpos;
								if (rp_old.ItemIndex != new_ix)
								{
									//	Debug.Print("child:  rail {0}:  ix{1} -> ix{2}", child_lev, rp_old.RailIndex, new_ix);

									//rp_old.Min = null;
									//rp_old.Extra = null;

									rp_old.MoveTo(ref new_ix);
									cmov++;
								}

								new_ix++;
							}

							double dx = rgch[rgch.Length - 1].railpos.RailCalcs().Right.Value - rgch[0].railpos.RailCalcs().Left.Value;
							double dxx = rgch[0].railpos.RailCalcs().Left.Value + (dx / 2);

							int zz = rails[node.Level].FindIndex(dxx);
							if (zz != node.railpos.ItemIndex)
							{
								//Debug.Print("parent:  rail {0}:  ix{1} -> ix{2}", node.Level, node.railpos.RailIndex, zz);
								node.railpos.MoveTo(ref zz);
								pmov++;
							}

							//node.railpos.Min = rgch[0].railpos.Left;
							//node.railpos.Extra = new BvdCenterNode(rgch[0].railpos.Left, rgch[rgch.Length - 1].railpos.Right, node.railpos.Width);

							//foreach (LayoutDag.Node n in dag)
							//{
							//     children moved to consecutive on rail
							//     first child : max (rail position, 
							//     parent-left determined by children
							//}
						}
					}
				}
				//Debug.Print("cmov: {0}  pmov: {1}", cmov, pmov);
			}
#endif
			double x_min = _dag.Min(n => n.Final.Left);
			double x_max = _dag.Max(n => n.Final.Right);
			double y_max = _dag.Max(n => n.Final.Bottom);

			double xdx = x_max - x_min;

			RenderTransform = new TranslateTransform(-x_min, 0);
			return new Size(xdx, y_max);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override Size ArrangeOverride(Size finalSize)
		{
			if (_dag.Count == 0)
				return finalSize;

			bool f_any = false;
			foreach (var li in _dag)
			{
				Debug.Assert(VisualTreeHelper.GetParent(li.Item) == this);

				Vector v = VisualTreeHelper.GetOffset(li.Item);
				Rect f = li.Final;
				if (f.Left != v.X || f.Top != v.Y || f.Width != li.Item.ActualWidth || f.Height != li.Item.ActualHeight)
				{
					li.Item.Arrange(li.Final);
					f_any = true;
				}

#if true
				Grid g = new Grid();
				g.ColumnDefinitions.Add(new ColumnDefinition());
				g.ColumnDefinitions.Add(new ColumnDefinition());
				g.ColumnDefinitions.Add(new ColumnDefinition());
				int row = 0;
				foreach (String x in li.railpos.ToString().Split(new String[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries))
				{
					String[] rg2 = x.Split(new Char[] { ':', ' ' }, StringSplitOptions.RemoveEmptyEntries);
					g.RowDefinitions.Add(new RowDefinition());

					g.Children.Add(Anon<TextBlock>.New(tb =>
						{
							tb.Text = rg2[0];
							tb.Margin = new Thickness(2, 0, 2, 0);
							Grid.SetColumn(tb, 0);
							Grid.SetRow(tb, row);
						}));
					if (rg2.Length > 1)
						g.Children.Add(Anon<TextBlock>.New(tb =>
						{
							tb.Text = rg2[1];
							tb.Margin = new Thickness(2, 0, 2, 0);
							tb.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
							Grid.SetColumn(tb, 1);
							Grid.SetRow(tb, row);
						}));
					if (rg2.Length > 2)
						g.Children.Add(Anon<TextBlock>.New(tb =>
						{
							tb.Text = rg2[2];
							tb.Margin = new Thickness(2, 0, 2, 0);
							tb.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
							Grid.SetColumn(tb, 2);
							Grid.SetRow(tb, row);
						}));
					row++;
				}

				li.Item.ToolTip = g;
				li.Item.SetValue(ToolTipService.InitialShowDelayProperty, 0);
				li.Item.SetValue(ToolTipService.ShowDurationProperty, 360000);
#endif
			}
			if (f_any)
				InvalidateVisual();
			return finalSize;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override void OnRender(DrawingContext dc)
		{
			base.OnRender(dc);

			if (_dag == null)
				return;

			Pen pen = new Pen(Stroke, StrokeThickness);

			Pen dashpen = new Pen(Stroke, StrokeThickness);
			dashpen.DashStyle = DashStyles.Dash;

			foreach (var li in _dag)
			{
				li.DoRender(dc, pen, dashpen);
			}
		}
	};
}